﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
/*
 * Representa valores de varios tipos
 * Ofereçe conversão flexivel do string para um valor
 * 
 */

namespace Sintaxe.Simbolos
{
    public enum TipoVal
    {
        Vazio,
        Ilegal,
        Ident,
        NumInt,   // sem detalhes sobre tamanho ou possibilidade de sinal.
        Char,
        Bool,
        String
    }
    public class Valor
    {
        // representação interna
        //
        Int64 _ValNum;
        string _Lexema;
        bool _ValBool;
        char _ValChar;
        string _ValString;
        TipoVal _Tipo;
        string _Mensagem;


        // infraestrutura
        //
        static Regex reIdent;

        // interface externo
        //
        public Valor(string prmValor) // consructor. Executa a conversão
        {
            string ss;

            // converter o parâmetro para a estrutura de um Valor
            //
            if ((prmValor == null) || (prmValor == ""))
            {
                _Tipo = TipoVal.Vazio;
                return;
            }
            try
            {
                _ValNum = Convert.ToInt64(prmValor); // decimal pos ou neg
                _Tipo = TipoVal.NumInt;
                return;
            }
            catch (Exception) { }

            // alternativas numéricas: dec, hex, bin
            try
            {
                _ValNum = Convert.ToInt64(prmValor, 16);  // hex pos
                _Tipo = TipoVal.NumInt;
                return;
            }
            catch (Exception) { }
            //try
            //{
            //    _ValNum = Convert.ToInt64(prmValor, 2);  // bin pos  ######### versão átual do Visual Studio não suporta 0b010101
            //    _Tipo = TipoVal.NumInt;
            //    return;
            //}
            //catch (Exception) { }

            // é identificador?
            if (reIdent == null)  // var estát. inicializado uma única vez
            {
                string start = @"(\p{Lu}|\p{Ll}|_)";
                // upper, lower, underline. No Title, Others, Punctuation-Connection,..
                string extend = @"(\p{Nd})";
                // decimal digit. No roman numbers, persian numbers, ...
                reIdent = new Regex(string.Format("{0}({0}|{1})*", start, extend));
            }

            ss = prmValor.Trim();
            Match mm = reIdent.Match(ss);
            if ((mm.Success) & (mm.Length == ss.Length))  // identificação total
            {
                _Tipo = TipoVal.Ident;
                _Lexema = ss;
                return;
            }

            // ############### ainda faltam
            // é literal booleano?
            // é literal string?
            // é literal charater?

            _Tipo = TipoVal.Ilegal;
            _Mensagem = String.Format("Não tem como converter {0} em um valór de tipo reconhecido.", prmValor);
            return;
        }
        public bool IsFixup(uint prmTam)
        {
            // testar se o Valor pudesse ser convertido em um Fixup de prmTam bytes
            if ((prmTam < 1) | (prmTam > 8))
            {
                _Mensagem = String.Format("Tamanho de {0} bytes é ilegal para um fixup.", prmTam);
                return false;
            }
            if (_Tipo == TipoVal.Ident) return true;  // mas relocação mais tarde poderia ainda descobrir problemas
            if (_Tipo == TipoVal.NumInt)
            {
                if (_ValNum < 0)
                {
                    _Mensagem = String.Format("Valor negativo {0} ilegal para fixup", _ValNum);
                    return false;
                }
                if (prmTam == 8) return true; // cada Int64 cabe in 8 bytes
                if (_ValNum <= (1L << (8 * (int)prmTam))) return true;
                _Mensagem = String.Format("Valor {0} não cabe em {1} bytes.", _ValNum, prmTam);
                return false;
            }

            // outros típos não produzem fixup
            _Mensagem = String.Format("Tipo {0} de Valor não produz fixup.", _Tipo);
            return false;
        }
        //public FixupFonte ToFixup(uint prmTam)
        //{
        //    // produzir um fixup do Valor

        //    FixupFonte ff;

        //    if (!IsFixup(prmTam))  // efeito colateral: deixar _Mensagem
        //    {
        //        throw new ApplicationException(_Mensagem);
        //    }

        //    if (_Tipo == TipoVal.Ident) ff = new FixupFonte(prmTam, _ValString);
        //    else ff = new FixupFonte(prmTam, (UInt64) _ValNum);
        //    return ff;
        //}
        public Int64 ValNum
        {
            get { return _ValNum; }
        }
        public string Lexema
        {
            get { return _Lexema; }
        }
        public bool ValBool
        {
            get { return _ValBool; }
        }
        public char ValChar
        {
            get { return _ValChar; }
        }
        public string ValString
        {
            get { return _ValString; }
        }
        public TipoVal Tipo
        {
            get { return _Tipo; }
        }
        public string Mensagem
        {
            get { return _Mensagem; }
        }
    }
}
